-- -------------------------------------------------------------------
--																	--
--			== Magic.lua ==											--
--																	--
--  Contains functions for magical damage, resistance, etc...		--
--																	--
-- -------------------------------------------------------------------

require("scripts/globals/magicburst")
require("scripts/globals/status")
require("scripts/globals/weather")
require("scripts/globals/utils")

-- -------------------------------------------------------------------

	MMSG_BUFF_FAIL = 75;

-- -------------------------------------------------------------------
--																	--
--	== Magical Skill enumerators ==									--
--																	--
-- -------------------------------------------------------------------

	DIVINE_MAGIC_SKILL	 	= 32;
	HEALING_MAGIC_SKILL 	= 33;
	ENHANCING_MAGIC_SKILL 	= 34;
	ENFEEBLING_MAGIC_SKILL 	= 35;
	ELEMENTAL_MAGIC_SKILL 	= 36;
	DARK_MAGIC_SKILL 		= 37;
	NINJUTSU_SKILL          = 39;
	SUMM0NING_SKILL 	    = 38;
	SINGING_SKILL           = 40;
	STRING_SKILL            = 41;
	WIND_SKILL              = 42;
	BLUE_SKILL				= 43;

-- -------------------------------------------------------------------
--																	--
--	== Vanadiel day enumerators ==									--
--																	--
-- -------------------------------------------------------------------

	FIRESDAY		= 0;
	EARTHSDAY		= 1;
	WATERSDAY		= 2;
	WINDSDAY		= 3;
	ICEDAY			= 4;
	LIGHTNINGDAY	= 5;
	LIGHTSDAY		= 6;
	DARKSDAY		= 7;

-- -------------------------------------------------------------------
--																	--
--	== Element enumerators ==										--
--																	--
-- -------------------------------------------------------------------

	ELE_NONE		= 0;
	ELE_FIRE		= 1;
	ELE_EARTH		= 2;
	ELE_WATER		= 3;
	ELE_WIND		= 4;
	ELE_ICE			= 5;
	ELE_LIGHTNING	= 6;
	-- Added both because monsters TP moves call it thunder
	ELE_THUNDER		= 6;
	ELE_LIGHT		= 7;
	ELE_DARK		= 8;

	dayStrong = {FIRESDAY, EARTHSDAY, WATERSDAY, WINDSDAY, ICEDAY, LIGHTNINGDAY, LIGHTSDAY, DARKSDAY};
	dayWeak = {WATERSDAY, WINDSDAY, LIGHTNINGDAY, ICEDAY, FIRESDAY, EARTHSDAY, DARKSDAY, LIGHTSDAY};
	singleWeatherStrong = {WEATHER_HOT_SPELL, WEATHER_DUST_STORM, WEATHER_RAIN, WEATHER_WIND, WEATHER_SNOW, WEATHER_THUNDER, WEATHER_AURORAS, WEATHER_GLOOM};
	doubleWeatherStrong = {WEATHER_HEAT_WAVE, WEATHER_SAND_STORM, WEATHER_SQUALL, WEATHER_GALES, WEATHER_BLIZZARDS, WEATHER_THUNDERSTORMS, WEATHER_STELLAR_GLARE, WEATHER_DARKNESS};
	singleWeatherWeak = {WEATHER_RAIN, WEATHER_WIND, WEATHER_THUNDER, WEATHER_SNOW, WEATHER_HOT_SPELL, WEATHER_DUST_STORM, WEATHER_GLOOM, WEATHER_AURORAS};
	doubleWeatherWeak = {WEATHER_SQUALL, WEATHER_GALES, WEATHER_THUNDERSTORMS, WEATHER_BLIZZARDS, WEATHER_HEAT_WAVE, WEATHER_SAND_STORM, WEATHER_DARKNESS, WEATHER_STELLAR_GLARE};
	elementalObi = {15435, 15438, 15440, 15437, 15436, 15439, 15441, 15442};
	elementalObiWeak = {15440, 15437, 15439, 15436, 15435, 15438, 15442, 15441};
	spellAcc = {MOD_FIREACC, MOD_EARTHACC, MOD_WATERACC, MOD_WINDACC, MOD_ICEACC, MOD_THUNDERACC, MOD_LIGHTACC, MOD_DARKACC};
	strongAffinity = {MOD_FIRE_AFFINITY, MOD_EARTH_AFFINITY, MOD_WATER_AFFINITY, MOD_WIND_AFFINITY, MOD_ICE_AFFINITY, MOD_THUNDER_AFFINITY, MOD_LIGHT_AFFINITY, MOD_DARK_AFFINITY};
	weakAffinity = {MOD_WATER_AFFINITY, MOD_WIND_AFFINITY, MOD_THUNDER_AFFINITY, MOD_ICE_AFFINITY, MOD_FIRE_AFFINITY, MOD_EARTH_AFFINITY, MOD_DARK_AFFINITY, MOD_LIGHT_AFFINITY};
	resistMod = {MOD_FIRERES, MOD_EARTHRES, MOD_WATERRES, MOD_WINDRES, MOD_ICERES, MOD_THUNDERRES, MOD_LIGHTRES, MOD_DARKRES};
	defenseMod = {MOD_FIREDEF, MOD_EARTHDEF, MOD_WATERDEF, MOD_WINDDEF, MOD_ICEDEF, MOD_THUNDERDEF, MOD_LIGHTDEF, MOD_DARKDEF};
    absorbMod = {MOD_FIRE_ABSORB, MOD_EARTH_ABSORB, MOD_WATER_ABSORB, MOD_WIND_ABSORB, MOD_ICE_ABSORB, MOD_LTNG_ABSORB, MOD_LIGHT_ABSORB, MOD_DARK_ABSORB};
    nullMod = {MOD_FIRE_NULL, MOD_EARTH_NULL, MOD_WATER_NULL, MOD_WIND_NULL, MOD_ICE_NULL, MOD_LTNG_NULL, MOD_LIGHT_NULL, MOD_DARK_NULL};

-- -------------------------------------------------------------------
--																	--
--	== calculateMagicDamage == USED FOR DAMAGING MAGICAL SPELLS		--
--	(Stages 1 and 2 in Calculating Magic Damage on wiki)			--
--																	--
--	Calculates magic damage using the standard magic damage calc.	--
--	Does NOT handle resistance.										--
--																	--
--	Inputs:															--
--	V - The base damage of the spell.	 							--
--	M - The INT multiplier of the spell.							--
--	skilltype - The skill ID of the spell.							--
--	atttype - The attribute type (usually MOD_INT , except for		--
--	things like Banish which is MOD_MND).							--
--	hasMultipleTargetReduction - true ifdamage is reduced on AoE.	--
--	False otherwise (e.g. Charged Whisker vs -ga3 spells).			--
--																	--
--	Output:															--
--	The total damage, before resistance and before equipment (so no	--
--	HQ staff bonus worked out here).								--
--																	--
-- -------------------------------------------------------------------

	SOFT_CAP = 60; --guesstimated
	HARD_CAP = 120; --guesstimated

function calculateMagicDamage(V,M,player,spell,target,skilltype,atttype,hasMultipleTargetReduction)

	local dint = player:getStat(atttype) - target:getStat(atttype);
	local dmg = V + player:getMod(MOD_MAG_DMG_STAT);

	if(dint<=0) then -- If dINT penalises, it's always M=1
		dmg = dmg + dint;
        if(dmg <= 0) then --dINT penalty cannot result in negative damage (target absorption)
            return 0;
        end
	elseif(dint > 0 and dint <= SOFT_CAP) then -- The standard calc, most spells hit this
		dmg = dmg + (dint*M);
	elseif(dint > 0 and dint > SOFT_CAP and dint < HARD_CAP) then -- After SOFT_CAP, INT is only half effective
		dmg = dmg + SOFT_CAP*M + ((dint-SOFT_CAP)*M)/2;
	elseif(dint > 0 and dint > SOFT_CAP and dint >= HARD_CAP) then -- After HARD_CAP, INT has no effect.
		dmg = dmg + HARD_CAP*M;
	end

	if(skilltype == DIVINE_MAGIC_SKILL and target:isUndead()) then
		-- 150% bonus damage
		dmg = dmg * 1.5;
	end

	-- printf("dmg: %d dint: %d\n", dmg, dint);
	return dmg;

end;

-- -------------------------------------------------------------------
--																	--
--		Boost/Gain type spells										--
--																	--
-- -------------------------------------------------------------------

function doBoostGain(caster,target,spell,effect)
    local duration = 300;
    if (caster:hasStatusEffect(EFFECT_COMPOSURE) == true and caster:getID() == target:getID()) then
        duration = duration * 3;
    end
    
    --calculate potency
    local magicskill = target:getSkillLevel(ENHANCING_MAGIC_SKILL);

    local potency = math.floor((magicskill - 300) / 10) + 5; 
    
    if(potency > 25) then
        potency = 25;
    elseif(potency < 5) then
        potency = 5;
    end

    --printf("BOOST-GAIN: POTENCY = %d", potency);
    
    --Only one Boost Effect can be active at once, so if the player has any we have to cancel & overwrite
    local effectOverwrite = {80, 81, 82, 83, 84, 85, 86};
    
    for i, effect in ipairs(effectOverwrite) do
            --printf("BOOST-GAIN: CHECKING FOR EFFECT %d...",effect);
            if(caster:hasStatusEffect(effect)) then
                --printf("BOOST-GAIN: HAS EFFECT %d, DELETING...",effect);
                caster:delStatusEffect(effect);
            end
    end
    
    if(target:addStatusEffect(effect,potency,0,duration)) then
        spell:setMsg(230);
    else
        spell:setMsg(75);
    end
end;

-- -------------------------------------------------------------------
--																	--
--		Enspell your weapon.										--
--																	--
-- -------------------------------------------------------------------

function doEnspell(caster,target,spell,effect)

	if(effect==EFFECT_BLOOD_WEAPON) then
		target:addStatusEffect(EFFECT_BLOOD_WEAPON,1,0,30);
		return;
	end

	local duration = 180;
	duration = duration + target:getMod(MOD_ENSPELL_DURATION);
	-- Estoqueurs Bonus
	duration = duration + (duration * caster:getMod(MOD_ENHANCING_DUR));

	if (caster:hasStatusEffect(EFFECT_COMPOSURE) == true and caster:getID() == target:getID()) then
		duration = duration * 3;
	end

	-- Calculate potency
	local magicskill = target:getSkillLevel(ENHANCING_MAGIC_SKILL);

	local potency = 3 + math.floor((6*magicskill)/100);
	if(magicskill>200) then
		potency = 5 + math.floor((5*magicskill)/100);
	end

	if(target:addStatusEffect(effect,potency,0,duration)) then
		spell:setMsg(230);
	else
		spell:setMsg(75);
	end
end;


-- -------------------------------------------------------------------
--																	--
--		== Cure Spell Functions ==									--
--  getCurePower returns the caster's cure power					--
--  getCureFinal returns the final cure amount						--
--	Author: ZeDingo													--
--	Source:															--
--  http://members.shaw.ca/pizza_steve/cure/Cure_Calculator.html	--
--																	--
-- -------------------------------------------------------------------

 function getCurePower(caster,isBlueMagic)
	local MND = caster:getStat(MOD_MND);
	local VIT = caster:getStat(MOD_VIT);
	local skill = caster:getSkillLevel(HEALING_MAGIC_SKILL) + caster:getMod(MOD_HEALING);
	local power = math.floor(MND/2) + math.floor(VIT/4) + skill;
	return power;
end;

function getCurePowerOld(caster)
	local MND = caster:getStat(MOD_MND);
	local VIT = caster:getStat(MOD_VIT);
	local skill = caster:getSkillLevel(HEALING_MAGIC_SKILL) + caster:getMod(MOD_HEALING);--it's healing magic skill for the BLU cures as well
	local power = ((3 * MND) + VIT + (3 * math.floor(skill/5)));
	return power;
end;

function getBaseCure(power,divisor,constant,basepower)
	return ((power - basepower) / divisor) + constant;
end;

function getBaseCureOld(power,divisor,constant)
	return (power / 2) / divisor + constant;
end;

function getCureFinal(caster,spell,basecure,minCure,isBlueMagic)
	if(basecure < minCure) then
		basecure = minCure;
	end

	local potency = 1 + (caster:getMod(MOD_CURE_POTENCY) / 100);
	if(potency > 1.5) then
		potency = 1.5;
	end

	local dSeal = 1;
	if (caster:hasStatusEffect(EFFECT_DIVINE_SEAL)) then
		dSeal = 2;
	end

	local rapture = 1;
	if(isBlueMagic == false) then --rapture doesn't affect BLU cures as they're not white magic
		if (caster:hasStatusEffect(EFFECT_RAPTURE)) then
			local equippedHead = caster:getEquipID(SLOT_HEAD);
			if(equippedHead == 11183) then
				rapture = 1.55; --savant's bonnet +1
			elseif(equippedHead == 11083) then
				rapture = 1.6; --savant's bonnet +2
			else
				rapture = 1.5;
			end
			caster:delStatusEffectSilent(EFFECT_RAPTURE);
		end
	end
	local dayWeatherBonus = 1;
	local ele = spell:getElement();

	local castersWeather = caster:getWeather();
	local equippedMain = caster:getEquipID(SLOT_MAIN);
	local equippedWaist = caster:getEquipID(SLOT_WAIST);

	if(castersWeather == singleWeatherStrong[ele]) then
		if(equippedMain == 18632 or equippedMain == 18633) then
			if(math.random() < 0.33 or equippedWaist == elementalObi[ele]) then
				dayWeatherBonus = dayWeatherBonus + 0.10;
			end
		end
		if(math.random() < 0.33 or equippedWaist == elementalObi[ele]) then
			dayWeatherBonus = dayWeatherBonus + 0.10;
		end
	elseif(castersWeather == singleWeatherWeak[ele]) then
		if(math.random() < 0.33 or equippedWaist == elementalObi[ele]) then
			dayWeatherBonus = dayWeatherBonus - 0.10;
		end
	elseif(castersWeather == doubleWeatherStrong[ele]) then
		if(equippedMain == 18632 or equippedMain == 18633) then
			if(math.random() < 0.33 or equippedWaist == elementalObi[ele]) then
				dayWeatherBonus = dayWeatherBonus + 0.10;
			end
		end
		if(math.random() < 0.33 or equippedWaist == elementalObi[ele]) then
			dayWeatherBonus = dayWeatherBonus + 0.25;
		end
	elseif(castersWeather == doubleWeatherWeak[ele]) then
		if(math.random() < 0.33 or equippedWaist == elementalObi[ele]) then
			dayWeatherBonus = dayWeatherBonus - 0.25;
		end
	end

	local dayElement = VanadielDayElement();
	if(dayElement == dayStrong[ele]) then
		if(math.random() < 0.33 or equippedWaist == elementalObi[ele]) then
			dayWeatherBonus = dayWeatherBonus + 0.10;
		end
	elseif(dayElement == dayWeak[ele]) then
		if(math.random() < 0.33 or equippedWaist == elementalObi[ele]) then
			dayWeatherBonus = dayWeatherBonus - 0.10;
		end
	end

	if(dayWeatherBonus > 1.35) then
		dayWeatherBonus = 1.35;
	end

	local final = math.floor(math.floor(math.floor(math.floor(basecure) * potency) * dayWeatherBonus) * rapture) * dSeal;
	return final;
end;

function getCureAsNukeFinal(caster,spell,power,divisor,constant,basepower)
	return getCureFinal(caster,spell,power,divisor,constant,basepower);
end;

-- -------------------------------------------------------------------
--																	--
--		== Elemental Affinity functions ==							--
--		Returns the staff bonus for the caster and spell.			--
--		Affinities that strengthen/weaken the index element			--
--		Author: ReaperX												--
--																	--
-- -------------------------------------------------------------------

function AffinityBonus(caster,ele)

	local bonus = 1.00;

	local affinity = caster:getMod(strongAffinity[ele]) - caster:getMod(weakAffinity[ele]);

	-- Iridal and Chatoyant will return affinity for strong and weak, cancelling their bonus out,
	-- so they need to be specifically checked.
	-- Could do an if strong == weak, but that would cause problems once/if augments or magian
	-- gear is added.

	local equippedMain = caster:getEquipID(SLOT_MAIN);
	local job = caster:getMainJob();
	local sjob = caster:getSubJob();

	if(caster:getObjType() == TYPE_PC) then
		if(job == JOB_BLM or sjob == JOB_BLM) then
			if(ele == 1) then
				affinity = affinity + caster:getMerit(MERIT_FIRE_MAGIC_POTENCY);
			elseif(ele == 2) then
				affinity = affinity + caster:getMerit(MERIT_EARTH_MAGIC_POTENCY);
			elseif(ele == 3) then
				affinity = affinity + caster:getMerit(MERIT_WATER_MAGIC_POTENCY);
			elseif(ele == 4) then
				affinity = affinity + caster:getMerit(MERIT_WIND_MAGIC_POTENCY);
			elseif(ele == 5) then
				affinity = affinity + caster:getMerit(MERIT_ICE_MAGIC_POTENCY);
			elseif(ele == 6) then
				affinity = affinity + caster:getMerit(MERIT_LIGHTNING_MAGIC_POTENCY);
			else
				affinity = affinity;
			end
		end
	end
	if (equippedMain == 18632) then
		affinity = affinity + 1;
	elseif (equippedMain == 18633) then
		affinity = affinity + 2;
	end

	if(affinity > 0) then
		bonus = bonus + 0.05 + 0.05 * affinity;
	elseif(affinity < 0) then
		bonus = bonus - 0.05 + 0.05 * affinity;
	end

	return bonus;

end;

-- -------------------------------------------------------------------
--																	--
--	== applyResistance == USED FOR DAMAGING MAGICAL SPELLS			--
--	(Stage 3 in Calculating Magic Damage on wiki)					--
--																	--
--	Reduces damage if it resists.									--
--																	--
--	Output:															--
--	The factor to multiply down damage (1/2 1/4 1/8 1/16) - In this --
--	format so this function can be used for enfeebles on duration.	--
--																	--
-- -------------------------------------------------------------------

function applyResistance(player,spell,target,diff,skill,bonus)
	-- Resist everything if magic shield is active
	if(target:hasStatusEffect(EFFECT_MAGIC_SHIELD, 0)) then
		return 0;
	end
	local resist = 1.0;
	local magicaccbonus = 0;
	local ele = spell:getElement();
	local weather = player:getWeather();

	if(bonus ~= nil) then
		magicaccbonus = magicaccbonus + bonus;
	end

    if (skill == SINGING_SKILL and player:hasStatusEffect(EFFECT_TROUBADOUR)) then
        if (math.random(0,99) < player:getMerit(MERIT_TROUBADOUR)-25) then
            return 1.0;
        end
    end

	-- Get the base acc (just skill plus magic acc mod)
	local magicacc = player:getSkillLevel(skill) + player:getMod(79 + skill) + player:getMod(MOD_MACC);
	if player:hasStatusEffect(EFFECT_ALTRUISM) and spell:getSpellGroup() == SPELLGROUP_WHITE then
		magicacc = magicacc + player:getStatusEffect(EFFECT_ALTRUISM):getPower();
	end
	if player:hasStatusEffect(EFFECT_FOCALIZATION) and spell:getSpellGroup() == SPELLGROUP_BLACK then
		magicacc = magicacc + player:getStatusEffect(EFFECT_FOCALIZATION):getPower();
	end
	-- Difference in int/mnd
	if(diff > 10) then
		magicacc = magicacc + 10 + (diff - 10)/2;
	else
		magicacc = magicacc + diff;
	end
	-- Add acc for Dark Seal/Troubadour/Divine Emblem
	if(player:getStatusEffect(EFFECT_DARK_SEAL) ~= nil and skill == DARK_MAGIC_SKILL) then
		magicaccbonus = magicaccbonus + 256;
		player:delStatusEffect(EFFECT_DARK_SEAL);
	elseif(player:getStatusEffect(EFFECT_DIVINE_EMBLEM) ~= nil and skill == DIVINE_MAGIC_SKILL) then
		magicaccbonus = magicaccbonus + 128;
	elseif(player:getStatusEffect(EFFECT_KLIMAFORM) ~= nil) then
		if(ele == 1) then
			if(weather == 4 or weather == 5) then
				magicaccbonus = magicaccbonus + 256;
			end
		elseif(ele == 2) then
			if(weather == 8 or weather == 9) then
				magicaccbonus = magicaccbonus + 256;
			end
		elseif(ele == 3) then
			if(weather == 6 or weather == 7) then
				magicaccbonus = magicaccbonus + 256;
			end
		elseif(ele == 4) then
			if(weather == 10 or weather == 11) then
				magicaccbonus = magicaccbonus + 256;
			end
		elseif(ele == 5) then
			if(weather == 12 or weather == 13) then
				magicaccbonus = magicaccbonus + 256;
			end
		elseif(ele == 6) then
			if(weather == 14 or weather == 15) then
				magicaccbonus = magicaccbonus + 256;
			end
		elseif(ele == 7) then
			if(weather == 16 or weather == 17) then
				magicaccbonus = magicaccbonus + 256;
			end
		elseif(ele == 8) then
			if(weather == 18 or weather == 19) then
				magicaccbonus = magicaccbonus + 256;
			end
		end
	elseif(player:getStatusEffect(EFFECT_TROUBADOUR) ~= nil) then
		local bardMerit = player:getMerit(MERIT_TROUBADOUR);
		local mAccBonus = 0;
		if(bardMerit == 50) then
			mAccBonus = 64;
		elseif(bardMerit == 75) then
			mAccBonus = 128;
		elseif(bardMerit == 100) then
			mAccBonus = 192;
		elseif(bardMerit == 125) then
			mAccBonus = 256;
		end
		magicaccbonus = magicaccbonus + mAccBonus;
	end

	-- Add Magical Accuracy Bonus from BLM AM II Spells
	local id = spell:getID();
	local merit = 0;

	if(player:getObjType() == TYPE_PC) then
		if(id == 207 or id == 209 or id == 211 or id == 213 or id == 215 or id == 205) then
			if (id == 205) then
				merit = player:getMerit(MERIT_FLARE_II);
			elseif (id == 207) then
				merit = player:getMerit(MERIT_FREEZE_II);
			elseif (id == 209) then
				merit = player:getMerit(MERIT_TORNADO_II);
			elseif (id == 211) then
				merit = player:getMerit(MERIT_QUAKE_II);
			elseif (id == 213) then
				merit = player:getMerit(MERIT_BURST_II);
			elseif (id == 215) then
				merit = player:getMerit(MERIT_FLOOD_II);
			end
			magicaccbonus = magicaccbonus + (merit - 5);
		end

		-- Add Magical Accuracy Bonus from Merit RDM Spells
		if(job == JOB_RDM or sjob == JOB_RDM) then
			if(ele == 1) then
				magicaccbonus = magicaccbonus + player:getMerit(MERIT_FIRE_MAGIC_ACCURACY);
			elseif(ele == 2) then
				magicaccbonus = magicaccbonus + player:getMerit(MERIT_EARTH_MAGIC_ACCURACY);
			elseif(ele == 3) then
				magicaccbonus = magicaccbonus + player:getMerit(MERIT_WATER_MAGIC_ACCURACY);
			elseif(ele == 4) then
				magicaccbonus = magicaccbonus + player:getMerit(MERIT_WIND_MAGIC_ACCURACY);
			elseif(ele == 5) then
				magicaccbonus = magicaccbonus + player:getMerit(MERIT_ICE_MAGIC_ACCURACY);
			elseif(ele == 6) then
				magicaccbonus = magicaccbonus + player:getMerit(MERIT_LIGHTNING_MAGIC_ACCURACY);
			else
				magicaccbonus = magicaccbonus;
			end
		end


		if(id == 79) then
			merit = player:getMerit(MERIT_SLOW_II);
			magicaccbonus = magicaccbonus + (merit - 1);
		end


		if(id == 80 or id == 276) then
			if (id == 80) then
				merit = player:getMerit(MERIT_PARALYZE_II);
			elseif (id == 276) then
				merit = player:getMerit(MERIT_BLIND_II);
			end

			magicaccbonus = magicaccbonus + (merit - 5);
		end -- If RDM Merit Spells +

		-- add Magical Accuracy Bonus from Merit NIN Spells
		if(id == 322 or id == 325 or id == 328 or id == 331 or id == 334 or id == 337) then
			if(id == 322) then
				merit = player:getMerit(MERIT_KATON_SAN);
			elseif(id == 325) then
				merit = player:getMerit(MERIT_HYOTON_SAN);
			elseif(id == 328) then
				merit = player:getMerit(MERIT_HUTON_SAN);
			elseif(id == 331) then
				merit = player:getMerit(MERIT_DOTON_SAN);
			elseif(id == 334) then
				merit = player:getMerit(MERIT_RAITON_SAN);
			elseif(id == 337) then
				merit = player:getMerit(MERIT_SUITON_SAN);
			end
		end
		magicaccbonus = magicaccbonus + ((merit - 1)*5);
	end -- If NIN Merit Spells +

	-- Add acc for staves
	local affinityBonus = AffinityBonus(player, spell:getElement());
	magicaccbonus = magicaccbonus + (affinityBonus-1) * 200;

	local skillchainTier, skillchainCount = FormMagicBurst(spell:getElement(), target);
	-- Add acc for skillchains
	if(skillchainTier > 0) then
		magicaccbonus = magicaccbonus + 25;
	end

	-- Base magic evasion (base magic evasion plus resistances(players), plus elemental defense(mobs)
	local magiceva = target:getMod(MOD_MEVA) + target:getMod(resistMod[spell:getElement()]);
	-- Get the difference of acc and eva, scale with level (3.33 at 10 to 0.44 at 75)
	local multiplier = 0;
	if player:getMainLvl() < 40 then
		multiplier = 100 / 120;
	else
		multiplier = 100 / (player:getMainLvl() * 3);
	end;
	local p = (magicacc * multiplier) - (magiceva * 0.45);
	magicaccbonus = magicaccbonus / 2;
	-- Add magicacc bonus
	p = p + magicaccbonus;
    -- printf("acc: %f, eva: %f, bonus: %f", magicacc, magiceva, magicaccbonus);

	-- Double any acc over 50 if it's over 50
	if(p > 5) then
		p = 5 + (p - 5) * 2;
	end

	-- Add a flat bonus that won't get doubled in the previous step
	p = p + 45;

	-- Add a scaling bonus or penalty based on difference of targets level from caster
	local leveldiff = player:getMainLvl() - target:getMainLvl();
	if(leveldiff < 0) then
		p = p - (25 * ( (player:getMainLvl()) / 75 )) + leveldiff;
	else
		p = p + (25 * ( (player:getMainLvl()) / 75 )) + leveldiff;
	end
	--cap accuracy
	if(p > 95) then
		p = 95;
	elseif(p < 5) then
		p = 5;
	end

	p = p / 100;

	-- Resistance thresholds based on p.  A higher p leads to lower resist rates, and a lower p leads to higher resist rates.
	local half = (1 - p);
	local quart = ((1 - p)^2);
	local eighth = ((1 - p)^3);
	local sixteenth = ((1 - p)^4);
	-- print("HALF:",half);
	-- print("QUART:",quart);
	-- print("EIGHTH:",eighth);
	-- print("SIXTEENTH:",sixteenth);

	local resvar = math.random();
	-- Determine final resist based on which thresholds have been crossed.
	if(resvar <= sixteenth) then
		resist = 0.0625;
		-- printf("Spell resisted to 1/16!!!  Threshold = %u",sixteenth);
	elseif(resvar <= eighth) then
		resist = 0.125;
		-- printf("Spell resisted to 1/8!  Threshold = %u",eighth);
	elseif(resvar <= quart) then
		resist = 0.25;
		-- printf("Spell resisted to 1/4.  Threshold = %u",quart);
	elseif(resvar <= half) then
		resist = 0.5;
		-- printf("Spell resisted to 1/2.  Threshold = %u",half);
	else
		resist = 1.0;
		-- printf("1.0");
	end

	return resist;

end;

-- -------------------------------------------------------------------
--																	--
--	== applyResistanceAbility == USED FOR ABILITIES					--
--																	--
--	Applies resistance for things that may not be spells.			--
--	ie. Quick Draw													--
--																	--
--	Output:															--
--	The factor to multiply down damage (1/2 1/4 1/8 1/16) - In this --
--	format so this function can be used for enfeebles on duration.	--
--																	--
-- -------------------------------------------------------------------

function applyResistanceAbility(player,target,element,skill,bonus)
	-- Resist everything if magic shield is active
	if(target:hasStatusEffect(EFFECT_MAGIC_SHIELD, 0)) then
		return 0;
	end

	local resist = 1.0;
	local magicaccbonus = 0;

	if(bonus ~= nil) then
		magicaccbonus = magicaccbonus + bonus;
	end

	-- Get the base acc (just skill plus magic acc mod)
	local magicacc = player:getSkillLevel(skill) + player:getMod(79 + skill) + player:getMod(MOD_MACC);

	-- Add acc for staves
	local affinityBonus = AffinityBonus(player, element);
	magicaccbonus = magicaccbonus + (affinityBonus-1) * 200;

	-- Base magic evasion (base magic evasion plus resistances(players), plus elemental defense(mobs)
	local magiceva = target:getMod(MOD_MEVA) + target:getMod(resistMod[element]);

	-- Get the difference of acc and eva, scale with level (3.33 at 10 to 0.44 at 75)
	local multiplier = 0;
	if player:getMainLvl() < 40 then
		multiplier = 100 / 120;
	else
		multiplier = 100 / (player:getMainLvl() * 3);
	end;
	local p = (magicacc * multiplier) - (magiceva * 0.45);
	magicaccbonus = magicaccbonus / 2;
	-- Add magicacc bonus
	p = p + magicaccbonus;
	-- printf("acc: %f, eva: %f, bonus: %f", magicacc, magiceva, magicaccbonus);

	-- Double any acc over 50 if it's over 50
	if(p > 5) then
		p = 5 + (p - 5) * 2;
	end

	-- Add a flat bonus that won't get doubled in the previous step
	p = p + 45;

	-- Add a scaling bonus or penalty based on difference of targets level from caster
	local leveldiff = player:getMainLvl() - target:getMainLvl();
	if(leveldiff < 0) then
		p = p - (25 * ( (player:getMainLvl()) / 75 )) + leveldiff;
	else
		p = p + (25 * ( (player:getMainLvl()) / 75 )) + leveldiff;
	end
	-- Cap accuracy
	if(p > 95) then
		p = 95;
	elseif(p < 5) then
		p = 5;
	end

	p = p / 100;

	-- Resistance thresholds based on p.  A higher p leads to lower resist rates, and a lower p leads to higher resist rates.
	local half = (1 - p);
	local quart = ((1 - p)^2);
	local eighth = ((1 - p)^3);
	local sixteenth = ((1 - p)^4);
	-- print("HALF:",half);
	-- print("QUART:",quart);
	-- print("EIGHTH:",eighth);
	-- print("SIXTEENTH:",sixteenth);

	local resvar = math.random();

	-- Determine final resist based on which thresholds have been crossed.
	if(resvar <= sixteenth) then
		resist = 0.0625;
		-- printf("Spell resisted to 1/16!!!  Threshold = %u",sixteenth);
	elseif(resvar <= eighth) then
		resist = 0.125;
		-- printf("Spell resisted to 1/8!  Threshold = %u",eighth);
	elseif(resvar <= quart) then
		resist = 0.25;
		-- printf("Spell resisted to 1/4.  Threshold = %u",quart);
	elseif(resvar <= half) then
		resist = 0.5;
		-- printf("Spell resisted to 1/2.  Threshold = %u",half);
	else
		resist = 1.0;
		-- printf("1.0");
	end
	return resist;
end;

-- -------------------------------------------------------------------
--																	--
--	== applyResistanceAddEffect == USED FOR Additional Effects		--
--																	--
--	Output:															--
--	The factor to multiply down damage (1/2 1/4 1/8 1/16) - In this --
--	format so this function can be used for enfeebles on duration.	--
--																	--
-- -------------------------------------------------------------------

function applyResistanceAddEffect(player,target,element,bonus)

	local resist = 1.0;
	local magicaccbonus = 0;

	if(bonus ~= nil) then
		magicaccbonus = magicaccbonus + bonus;
	end

	-- Get the base acc (just skill plus magic acc mod)
	local magicacc = 0;

	-- Add acc for staves
	local affinityBonus = AffinityBonus(player, element);
	magicaccbonus = magicaccbonus + (affinityBonus-1) * 200;

	-- Base magic evasion (base magic evasion plus resistances(players), plus elemental defense(mobs)
	local magiceva = target:getMod(resistMod[element]);

	-- Get the difference of acc and eva, scale with level (3.33 at 10 to 0.44 at 75)
	local multiplier = 0;
	if player:getMainLvl() < 40 then
		multiplier = 100 / 120;
	else
		multiplier = 100 / (player:getMainLvl() * 3);
	end;
	local p = (magicacc * multiplier) - (magiceva * 0.45);
	magicaccbonus = magicaccbonus / 2;
	-- Add magicacc bonus
	p = p + magicaccbonus;
	-- printf("acc: %f, eva: %f, bonus: %f", magicacc, magiceva, magicaccbonus);

	-- Add a flat bonus that won't get doubled in the previous step
	p = p + 75;
	-- Add a scaling bonus or penalty based on difference of targets level from caster
	local leveldiff = player:getMainLvl() - target:getMainLvl();
	--[[if(leveldiff < 0) then
		p = p - (25 * ( (player:getMainLvl()) / 75 )) + leveldiff;
	else
		p = p + (25 * ( (player:getMainLvl()) / 75 )) + leveldiff;
	end]]
	p = p + leveldiff*2;
	-- Cap accuracy
	if(p > 95) then
		p = 95;
	elseif(p < 5) then
		p = 5;
	end

	p = p / 100;

	-- Resistance thresholds based on p.  A higher p leads to lower resist rates, and a lower p leads to higher resist rates.
	local half = (1 - p);
	local quart = ((1 - p)^2);
	local eighth = ((1 - p)^3);
	local sixteenth = ((1 - p)^4);
	--print("HALF: "..half);
	--print("QUART: "..quart);
	--print("EIGHTH: "..eighth);
	--print("SIXTEENTH: "..sixteenth);

	local resvar = math.random();

	-- Determine final resist based on which thresholds have been crossed.
	if(resvar <= sixteenth) then
		resist = 0.0625;
		-- printf("Spell resisted to 1/16!!!  Threshold = %u",sixteenth);
	elseif(resvar <= eighth) then
		resist = 0.125;
		-- printf("Spell resisted to 1/8!  Threshold = %u",eighth);
	elseif(resvar <= quart) then
		resist = 0.25;
		-- printf("Spell resisted to 1/4.  Threshold = %u",quart);
	elseif(resvar <= half) then
		resist = 0.5;
		-- printf("Spell resisted to 1/2.  Threshold = %u",half);
	else
		resist = 1.0;
		-- printf("1.0");
	end

	return resist;

end;

-- -------------------------------------------------------------------
--																	--
--		== SKILL LEVEL CALCULATOR ==								--
--																	--
--	Returns a skill level based on level and rating.				--
--	Author: Tenjou													--
--																	--
--	See the translation of aushacho's work by Themanii:				--
--		http://home.comcast.net/~themanii/skill.html				--
--																	--
--	The arguments are skill rank (numerical), and level.  1 is A+,	--
--	2 is A-, and so on.												--
--																	--
-- -------------------------------------------------------------------

function getSkillLvl(rank,level)

	local skill = 0; -- Failsafe

	if(level <= 50) then -- Levels 1-50
		if(rank == 1 or rank == 2) then -- A - Rated Skill
			skill = (((level-1)*3)+6);
		elseif(rank == 3 or rank == 4 or rank == 5) then -- B - Rated Skill
			skill = (((level-1)*2.9)+5);
		elseif(rank == 6 or rank == 7 or rank == 8) then -- C - Rated Skill
			skill = (((level-1)*2.8)+5);
		elseif(rank == 9) then -- D - Rated Skill
			skill = (((level-1)*2.7)+4);
		elseif(rank == 10) then -- E - Rated Skill
			skill = (((level-1)*2.5)+4);
		elseif(rank == 11) then -- F - Rated Skill
			skill = (((level-1)*2.3)+4);
		end
	elseif(level > 50 and level <= 60) then -- Levels 51-60
		if(rank == 1 or rank == 2) then -- A - Rated Skill
			skill = (((level-50)*5)+153);
		elseif(rank == 3 or rank == 4 or rank == 5) then -- B - Rated Skill
			skill = (((level-50)*4.9)+147);
		elseif(rank == 6 or rank == 7 or rank == 8) then -- C - Rated Skill
			skill = (((level-50)*4.8)+142);
		elseif(rank == 9) then -- D - Rated Skill
			skill = (((level-50)*4.7)+136);
		elseif(rank == 10) then -- E - Rated Skill
			skill = (((level-50)*4.5)+126);
		elseif(rank == 11) then -- F - Rated Skill
			skill = (((level-50)*4.3)+116);
		end
	elseif(level > 60 and level <= 70) then -- Levels 61-70
		if(rank == 1) then -- A+ Rated Skill
			skill = (((level-60)*4.85)+203);
		elseif(rank == 2) then -- A- Rated Skill
			skill = (((level-60)*4.10)+203);
		elseif(rank == 3) then -- B+ Rated Skill
			skill = (((level-60)*3.70)+196);
		elseif(rank == 4) then -- B Rated Skill
			skill = (((level-60)*3.23)+196);
		elseif(rank == 5) then -- B- Rated Skill
			skill = (((level-60)*2.70)+196);
		elseif(rank == 6) then -- C+ Rated Skill
			skill = (((level-60)*2.50)+190);
		elseif(rank == 7) then -- C Rated Skill
			skill = (((level-60)*2.25)+190);
		elseif(rank == 8) then -- C- Rated Skill
			skill = (((level-60)*2.00)+190);
		elseif(rank == 9) then -- D Rated Skill
			skill = (((level-60)*1.85)+183);
		elseif(rank == 10) then -- E Rated Skill
			skill = (((level-60)*1.95)+171);
		elseif(rank == 11) then -- F Rated Skill
			skill = (((level-60)*2.05)+159);
		end
	else -- Level 71 and above
		if(rank == 1) then -- A+ Rated Skill
			skill = (((level-70)*5)+251);
		elseif(rank == 2) then -- A- Rated Skill
			skill = (((level-70)*5)+244);
		elseif(rank == 3) then -- B+ Rated Skill
			skill = (((level-70)*3.70)+233);
		elseif(rank == 4) then -- B Rated Skill
			skill = (((level-70)*3.23)+228);
		elseif(rank == 5) then -- B- Rated Skill
			skill = (((level-70)*2.70)+223);
		elseif(rank == 6) then -- C+ Rated Skill
			skill = (((level-70)*3)+215);
		elseif(rank == 7) then -- C Rated Skill
			skill = (((level-70)*2.6)+212);
		elseif(rank == 8) then -- C- Rated Skill
			skill = (((level-70)*2.00)+210);
		elseif(rank == 9) then -- D Rated Skill
			skill = (((level-70)*1.85)+201);
		elseif(rank == 10) then -- E Rated Skill
			skill = (((level-70)*1.95)+190);
		elseif(rank == 11) then -- F Rated Skill
			skill = (((level-70)*2)+179);
		end
	end
	return skill;
end;

-- -------------------------------------------------------------------
--																	--
--		== Afflatus Misery ==										--
--																	--
-- -------------------------------------------------------------------

function handleAfflatusMisery(caster, spell, dmg)
	if(caster:hasStatusEffect(EFFECT_AFFLATUS_MISERY)) then
		local misery = caster:getMod(MOD_AFFLATUS_MISERY);

		--BGwiki puts the boost capping at 200% bonus at around 300hp
		if(misery > 300) then
			misery = 300;
		end;

		--So, if wee capped at 300, we'll make the boost it boost 2x (200% damage)                        
		local boost = 1 + (misery / 300);

		local preboost = dmg;

		dmg = math.floor(dmg * boost);
		
		--printf("AFFLATUS MISERY: Boosting %d -> %f, Final %d", preboost, boost, dmg);

		--Afflatus Mod is Used Up...
		caster:setMod(MOD_AFFLATUS_MISERY, 0)
	end
	return dmg;
end;
 

-- -------------------------------------------------------------------
--																	--
--		== finalMagicAdjustments ==									--
--																	--
--	Applies some damage bonuses and damage reduction adjustments.	--
--	AoE, Scarlet Delirium, Absorb & Annul magic, Mana Wall, etc.	--
--																	--
-- -------------------------------------------------------------------

function finalMagicAdjustments(caster,target,spell,dmg)
	local skill = spell:getSkillType();
	local element = spell:getElement();
	local ammo = target:getEquipID(SLOT_AMMO);
	local recoverMP = 0;
	local occultTP = 0;
	-- Handle multiple targets
	if(caster:isSpellAoE(spell:getID())) then
		local total = spell:getTotalTargets();

		if(total > 9) then
			-- ga spells on 10+ targets = 0.4
			dmg = dmg * 0.4;
		elseif(total > 1) then
			-- -ga spells on 2 to 9 targets = 0.9 - 0.05T where T = number of targets
			dmg = dmg * (0.9 - 0.05 * total);
		end

		-- Kill shadows
		-- target:delStatusEffect(EFFECT_COPY_IMAGE);
		-- target:delStatusEffect(EFFECT_BLINK);
	else
		-- this logic will eventually be moved here
		-- dmg = utils.takeShadows(target, dmg, 1);

		-- if(dmg == 0) then
			-- spell:setMsg(31);
			-- return 1;
		-- end
	end

	if(caster:hasStatusEffect(EFFECT_SCARLET_DELIRIUM_II) == true) then
		dmg = dmg + (dmg * caster:getMod(MOD_SCARLET_MDMG)/100);
	end

	if(caster:getObjType() == TYPE_PC) then
		if(caster:hasStatusEffect(EFFECT_ANCIENT_CIRCLE) == true and target:getSystem() == 10) then
			dmg = dmg + (dmg * caster:getMod(MOD_DRAGON_DMG)/100);
		elseif(caster:hasStatusEffect(EFFECT_ARCANE_CIRCLE) == true and target:getSystem() == 3) then
			dmg = dmg + (dmg * caster:getMod(MOD_ARCANE_DMG)/100);
		elseif(caster:hasStatusEffect(EFFECT_HOLY_CIRCLE) == true and target:getSystem() == 19) then
			dmg = dmg + (dmg * caster:getMod(MOD_UNDEAD_DMG)/100);
		elseif(caster:hasStatusEffect(EFFECT_WARDING_CIRCLE) == true and target:getSystem() == 9) then
			dmg = dmg + (dmg * caster:getMod(MOD_DEMON_DMG)/100);
		end
	elseif(caster:getObjType() == TYPE_MOB) then
		if(target:hasStatusEffect(EFFECT_ANCIENT_CIRCLE) == true and caster:getSystem() == 10) then
			dmg = dmg - (dmg * target:getMod(MOD_DRAGON_DMG)/100);
		elseif(target:hasStatusEffect(EFFECT_ARCANE_CIRCLE) == true and caster:getSystem() == 3) then
			dmg = dmg - (dmg * target:getMod(MOD_ARCANE_DMG)/100);
		elseif(target:hasStatusEffect(EFFECT_HOLY_CIRCLE) == true and caster:getSystem() == 19) then
			dmg = dmg - (dmg * target:getMod(MOD_UNDEAD_DMG)/100);
		elseif(target:hasStatusEffect(EFFECT_WARDING_CIRCLE) == true and caster:getSystem() == 9) then
			dmg = dmg - (dmg * target:getMod(MOD_DEMON_DMG)/100);
		end
	end

	dmg = utils.dmgTaken(target, dmg);
	dmg = utils.magicDmgTaken(target, dmg);
	if (dmg > 0) then
		dmg = dmg - target:getMod(MOD_PHALANX);
		utils.clamp(dmg, 0, 99999);
	end


	if(target:hasStatusEffect(EFFECT_SACROSANCTITY) == true) then
		dmg = utils.sacrosanctityDmg(target, dmg);
	end

	-- Handling stoneskin
	dmg = utils.stoneskin(target, dmg);

	if(target:hasStatusEffect(EFFECT_MANA_WALL) == true) then
		dmg = utils.manawall(target, dmg);
	end

	if(target:hasStatusEffect(EFFECT_SCARLET_DELIRIUM_I) == true) then
		utils.scarletDelirium(target, dmg);
	end

	utils.dmgToMP(target, dmg);

	dmg = utils.clamp(dmg, -99999, 99999);

	if (dmg < 0) then
		target:addHP(-dmg);
		dmg = -dmg;
		spell:setMsg(7);
	else
		target:delHP(dmg);
		target:updateEnmityFromDamage(caster,dmg);
	end

	-- Only add TP if the target is a mob
	if (target:getObjType() ~= TYPE_PC) then
		target:addTP(10);
	end

	if(skill == 36) then
		if(caster:getEquipID(SLOT_BODY) == 10286) then -- Seidr Cotehardie
			recoverMP = dmg * .02;
			caster:addMP(recoverMP);
		end
		-- printf("Cure MP Recovered %u",recoverMP);
		if(recoverMP > 0) then
			caster:messageBasic(25,0,recoverMP);
		end
	end

	if(skill == 36 or skill == 37) then
		if(caster:getMod(MOD_OCCULT_ACCUMEN) > 0) then
			local mpCost = spell:getMPCost();
			if(mpCost >= 100) then
				occultTP = (mpCost/100) * (caster:getMod(MOD_OCCULT_ACCUMEN)/1000);
				caster:addTP(occultTP);
			end
		end
	end
	return dmg;
end;

-- -------------------------------------------------------------------
--																	--
--		== finalMagicNonSpellAdjustments ==							--
--																	--
--	Applies some damage bonuses and damage reduction adjustments.	--
--	Absorb & Annul magic, Mana Wall, Sacrosancity etc.				--
--																	--
-- -------------------------------------------------------------------

function finalMagicNonSpellAdjustments(caster,target,ele,dmg)

	dmg = utils.dmgTaken(target, dmg);
	dmg = utils.magicDmgTaken(target, dmg);

	if(target:hasStatusEffect(EFFECT_SACROSANCTITY) == true) then
		dmg = utils.sacrosanctityDmg(target, dmg);
	end

	if (dmg > 0) then
		dmg = dmg - target:getMod(MOD_PHALANX);
		utils.clamp(dmg, 0, 99999);
	end

	-- Handling stoneskin
	dmg = utils.stoneskin(target, dmg);

	if(target:hasStatusEffect(EFFECT_MANA_WALL) == true) then
		dmg = utils.manawall(target, dmg);
	end

	if(target:hasStatusEffect(EFFECT_SCARLET_DELIRIUM_I) == true) then
		utils.scarletDelirium(target, dmg);
	end

	utils.dmgToMP(target, dmg);

	dmg = utils.clamp(dmg, -99999, 99999);

    if (dmg < 0) then
        target:addHP(-dmg);
    else
		target:delHP(dmg);
	end
	-- Not updating enmity from damage, as this is primarily used for additional effects
	-- (which don't generate emnity)
	-- In the case that updating enmity is needed, do it manually after calling this
	-- target:updateEnmityFromDamage(caster,dmg);

	return dmg;
end;

-- -------------------------------------------------------------------
--		== adjustForTarget ==	e.g. family % reduction				--
--																	--
--	Applies some damage reduction based on the target.				--
--																	--
-- -------------------------------------------------------------------

function adjustForTarget(target,dmg,ele)
    if (math.random(0,99) < target:getMod(absorbMod[ele]) or math.random(0,99) < target:getMod(MOD_MAGIC_ABSORB)) then
        return -dmg;
    end
    if (math.random(0,99) < target:getMod(nullMod[ele]) or math.random(0,99) < target:getMod(MOD_MAGIC_NULL)) then
        return 0;
    end
	return dmg;
end;

-- -------------------------------------------------------------------
--																	--
--		== calculateMagicBurstAndBonus ==							--
--																	--
--	Applies damage modifiers for Magic Bursts &						--
--	Ancient Magics Tier II											--
--																	--
-- -------------------------------------------------------------------

function calculateMagicBurstAndBonus(caster, spell, target)

	local burst = 1.0;
	local burstBonus = 1.0;

	if (spell:getSpellGroup() == 3 and not caster:hasStatusEffect(EFFECT_BURST_AFFINITY)) then
		return burst, burstBonus;
	end

	local skillchainTier, skillchainCount = FormMagicBurst(spell:getElement(), target);

	if(skillchainTier > 0) then
		if(skillchainCount == 1) then
			burst = 1.3;
		elseif(skillchainCount == 2) then
			burst = 1.35;
		elseif(skillchainCount == 3) then
			 burst = 1.40;
		elseif(skillchainCount == 4) then
			burst = 1.45;
		elseif(skillchainCount == 5) then
			burst = 1.50;
		else
			-- Something strange is going on if this occurs.
			burst = 1.0;
		end

		-- Get burst bonus from gear/spell bonus
		burstBonus = burstBonus + (target:getMod(MOD_MAG_BURST_BONUS)/100);

		-- This should be getting the spell ID, and checking
		-- if it is an Ancient Magic II spell.  Add 0.03
		-- to burstBonus for each merit the caster has for
		-- the given spell over the 1st merit.

		-- AM 2 get magic burst bonuses
		local id = spell:getID();
		local merit = 0;
		if(caster:getObjType() == TYPE_PC) then
			if(id == 207 or id == 209 or id == 211 or id == 213 or id == 215 or id == 205) then
				if (id == 205) then
					merit = caster:getMerit(MERIT_FLARE_II);
				elseif (id == 207) then
					merit = caster:getMerit(MERIT_FREEZE_II);
				elseif (id == 209) then
					merit = caster:getMerit(MERIT_TORNADO_II);
				elseif (id == 211) then
					merit = caster:getMerit(MERIT_QUAKE_II);
				elseif (id == 213) then
					merit = caster:getMerit(MERIT_BURST_II);
				elseif (id == 215) then
					merit = caster:getMerit(MERIT_FLOOD_II);
				end
		end
				if(merit == 5) then
					burstBonus = burstBonus;
				elseif(merit == 10) then
					burstBonus = burstBonus + 0.03;
				elseif(merit == 15) then
					burstBonus = burstBonus + 0.06;
				elseif(merit == 20) then
					burstBonus = burstBonus + 0.09;
				elseif(merit == 25) then
					burstBonus = burstBonus + 0.12;
				end
		end -- if AM2+
	end

	return burst, burstBonus;
end;

-- -------------------------------------------------------------------
--		== addBonuses ==	Damage modifiers for spells				--
--																	--
--	Applies damage modifiers for particular spells, spells of some	--
--	groups, some job abilities, some traits and merits.				--
--	Magic Critical hits.											--
--																	--
-- -------------------------------------------------------------------

function addBonuses(caster, spell, target, dmg, bonusmab)
	local ele = spell:getElement();
	local id = spell:getID();
	local skill = spell:getSkillType();
	local merit = 0;

	-- Elemental Mag Dmg Bonus (Spirit Lantern etc.)
	if(skill == 36) then
		dmg = math.floor(dmg * (1 + (caster:getMod(MOD_ELEM_MAG_DMG)/100)));
	end
	-- Enfeebling Mag Dmg Bonus (Estoquers Sayon +2 etc.)
	if(skill == 35) then
		dmg = math.floor(dmg * (1 + (caster:getMod(MOD_ENFB_MAG_DMG)/100)));
	end
	-- Drain / Aspir Dmg Bonus (Bounty Sickle etc.)
	if(id >= 245 and id <= 248) then
		dmg = math.floor(dmg * (1 + (caster:getMod(MOD_DRAIN_ASPIR)/100)));
		if(caster:hasStatusEffect(EFFECT_NETHER_VOID) == true) then
			dmg = dmg + (dmg / 2);
		end
	end
	-- Divine Emblem Holy Banish Dmg Bonus
	if(caster:getStatusEffect(EFFECT_DIVINE_EMBLEM) ~= nil and (id == 21 or id == 22 or
	(id >= 28 and id <= 32) or (id >= 38 and id <= 42))) then
		dmg = math.floor(dmg * (1 + (caster:getSkillLevel(DIVINE_MAGIC_SKILL)/100)));
		target:updateEnmity(caster,((spell:getCE() / 2) + (spell:getCE() * (caster:getMod(MOD_DIVINE_EMBLEM)/100)))
		,((spell:getVE() / 2) + (spell:getVE() * (caster:getMod(MOD_DIVINE_EMBLEM)/100))));
	end
	caster:delStatusEffect(EFFECT_DIVINE_EMBLEM);

	local affinityBonus = AffinityBonus(caster, spell:getElement());
	dmg = math.floor(dmg * affinityBonus);
	-- printf("Affinity Bonus DMG %u",dmg);
	local speciesReduction = target:getMod(defenseMod[ele]);
	speciesReduction = 1.00 - (speciesReduction/1000);
	dmg = math.floor(dmg * speciesReduction);
	-- printf("Species Reduction Bonus 2 DMG %u",dmg);
	local dayWeatherBonus = 1.00;
	local equippedMain = caster:getEquipID(SLOT_MAIN);
	local equippedWaist = caster:getEquipID(SLOT_WAIST);
	local weather = caster:getWeather();

	if(weather == singleWeatherStrong[ele]) then
		-- Iridescence
		if(equippedMain == 18632 or equippedMain == 18633) then
			if(math.random() < 0.33 or equippedWaist == elementalObi[ele] or isHelixSpell(spell)) then
				dayWeatherBonus = dayWeatherBonus + 0.10;
			end
		end
		if(math.random() < 0.33 or equippedWaist == elementalObi[ele] or isHelixSpell(spell)) then
			dayWeatherBonus = dayWeatherBonus + 0.10;
		end
	elseif(caster:getWeather() == singleWeatherWeak[ele]) then
		if(math.random() < 0.33 or equippedWaist == elementalObiWeak[ele] or isHelixSpell(spell)) then
			dayWeatherBonus = dayWeatherBonus - 0.10;
		end
	elseif(weather == doubleWeatherStrong[ele]) then
		-- Iridescence
		if(equippedMain == 18632 or equippedMain == 18633) then
			if(math.random() < 0.33 or equippedWaist == elementalObi[ele] or isHelixSpell(spell)) then
				dayWeatherBonus = dayWeatherBonus + 0.10;
			end
		end
		if(math.random() < 0.33 or equippedWaist == elementalObi[ele] or isHelixSpell(spell)) then
			dayWeatherBonus = dayWeatherBonus + 0.25;
		end
	elseif(weather == doubleWeatherWeak[ele]) then
		if(math.random() < 0.33 or equippedWaist == elementalObiWeak[ele] or isHelixSpell(spell)) then
			dayWeatherBonus = dayWeatherBonus - 0.25;
		end
	end

	local dayElement = VanadielDayElement();
	if(dayElement == dayStrong[ele]) then
		local equippedLegs = caster:getEquipID(SLOT_LEGS);
		if(equippedLegs == 15120 or equippedLegs == 15583) then
			dayWeatherBonus = dayWeatherBonus + 0.05;
		end
		if(math.random() < 0.33 or equippedWaist == elementalObi[ele] or isHelixSpell(spell)) then
			dayWeatherBonus = dayWeatherBonus + 0.10;
		end
	elseif(dayElement == dayWeak[ele]) then
		if(math.random() < 0.33 or equippedWaist == elementalObiWeak[ele] or isHelixSpell(spell)) then
			dayWeatherBonus = dayWeatherBonus + 0.10;
		end
	end

	if dayWeatherBonus > 1.35 then
		dayWeatherBonus = 1.35;
	end

	dmg = math.floor(dmg * dayWeatherBonus);

	local burst, burstBonus = calculateMagicBurstAndBonus(caster, spell, target);

	if(burst > 1.0) then
		spell:setMsg(spell:getMagicBurstMessage()); -- "Magic Burst!"
	end

	dmg = math.floor(dmg * burst);
	local mab = 0;

	if(spell:getID() >= 245 and spell:getID() <= 248) then
		mab = 1
	elseif (bonusmab ~= nil) then
		mab = (100 + caster:getMod(MOD_MATT) + bonusmab) / (100 + target:getMod(MOD_MDEF));
	else
		mab = (100 + caster:getMod(MOD_MATT)) / (100 + target:getMod(MOD_MDEF));
	end

	local magicCritRate = caster:getMod(MOD_MAGIC_CRIT_RATE);
	local magicCritBonus = 0;

	if(magicCritRate > 0) then
		if(math.random(100) >= magicCritRate) then
			magicCritBonus = 10 + caster:getMod(MOD_MAG_CRIT_DMG);
		end
	end

	local mab = (100 + caster:getMod(MOD_MATT) + magicCritBonus) / (100 + target:getMod(MOD_MDEF));

	if(mab < 0) then
		mab = 0;
	end

	-- Add Magical Attack Bonus from Merit NIN Spells
	if(caster:getObjType() == TYPE_PC) then
		if(id == 322 or id == 325 or id == 328 or id == 331 or id == 334 or id == 337) then
			if(id == 322) then
				merit = caster:getMerit(MERIT_KATON_SAN);
			elseif(id == 325) then
				merit = caster:getMerit(MERIT_HYOTON_SAN);
			elseif(id == 328) then
				merit = caster:getMerit(MERIT_HUTON_SAN);
			elseif(id == 331) then
				merit = caster:getMerit(MERIT_DOTON_SAN);
			elseif(id == 334) then
				merit = caster:getMerit(MERIT_RAITON_SAN);
			elseif(id == 337) then
				merit = caster:getMerit(MERIT_SUITON_SAN);
			end
			mab = mab + ((merit - 1)*5);
		end
	end -- If NIN Merit Spells +
	dmg = math.floor(dmg * mab);

	if (caster:hasStatusEffect(EFFECT_EBULLIENCE)) then
		local equippedHead = caster:getEquipID(SLOT_HEAD);
		if(equippedHead == 11183) then
			dmg = dmg * 1.25; --savant's bonnet +1
		elseif(equippedHead == 11083) then
			dmg = dmg * 1.3; --savant's bonnet +2
		else
			dmg = dmg * 1.2;
		end
		caster:delStatusEffectSilent(EFFECT_EBULLIENCE);
	end

	dmg = math.floor(dmg);

	-- print(affinityBonus);
	-- print(speciesReduction);
	-- print(dayWeatherBonus);
	-- print(burst);
	-- print(mab);
	-- print(magicDmgMod);
	if(caster:hasStatusEffect(EFFECT_NETHER_VOID)) then
		caster:delStatusEffect(EFFECT_NETHER_VOID);
	end
	return dmg;
end;

-- -------------------------------------------------------------------
--																	--
--		== addBonusesAbility ==	Damage modifiers for abilities		--
--																	--
--	Applies damage modifiers particular for some abilities.			--
--	Affinity, species, weather, etc...								--
--																	--
-- -------------------------------------------------------------------

function addBonusesAbility(caster, ele, target, dmg, params)

	local affinityBonus = AffinityBonus(caster, ele);
	dmg = math.floor(dmg * affinityBonus);

	local speciesReduction = target:getMod(defenseMod[ele]);
	speciesReduction = 1.00 - (speciesReduction/1000);
	dmg = math.floor(dmg * speciesReduction);

	local dayWeatherBonus = 1.00;
	local equippedMain = caster:getEquipID(SLOT_MAIN);
	local equippedWaist = caster:getEquipID(SLOT_WAIST);
	local weather = caster:getWeather();

	if(weather == singleWeatherStrong[ele]) then
		-- Iridescence
		if(equippedMain == 18632 or equippedMain == 18633) then
			if(math.random() < 0.33 or equippedWaist == elementalObi[ele] ) then
				dayWeatherBonus = dayWeatherBonus + 0.10;
			end
		end
		if(math.random() < 0.33 or equippedWaist == elementalObi[ele] ) then
			dayWeatherBonus = dayWeatherBonus + 0.10;
		end
	elseif(caster:getWeather() == singleWeatherWeak[ele]) then
		if(math.random() < 0.33 or equippedWaist == elementalObiWeak[ele] ) then
			dayWeatherBonus = dayWeatherBonus - 0.10;
		end
	elseif(weather == doubleWeatherStrong[ele]) then
		-- Iridescence
		if(equippedMain == 18632 or equippedMain == 18633) then
			if(math.random() < 0.33 or equippedWaist == elementalObi[ele] ) then
				dayWeatherBonus = dayWeatherBonus + 0.10;
			end
		end
		if(math.random() < 0.33 or equippedWaist == elementalObi[ele] ) then
			dayWeatherBonus = dayWeatherBonus + 0.25;
		end
	elseif(weather == doubleWeatherWeak[ele]) then
		if(math.random() < 0.33 or equippedWaist == elementalObiWeak[ele] ) then
			dayWeatherBonus = dayWeatherBonus - 0.25;
		end
	end

	local dayElement = VanadielDayElement();
	if(dayElement == dayStrong[ele]) then
		local equippedLegs = caster:getEquipID(SLOT_LEGS);
		if(equippedLegs == 15120 or equippedLegs == 15583) then
			dayWeatherBonus = dayWeatherBonus + 0.05;
		end
		if(math.random() < 0.33 or equippedWaist == elementalObi[ele] ) then
			dayWeatherBonus = dayWeatherBonus + 0.10;
		end
	elseif(dayElement == dayWeak[ele]) then
		if(math.random() < 0.33 or equippedWaist == elementalObiWeak[ele] ) then
			dayWeatherBonus = dayWeatherBonus + 0.10;
		end
	end

	if dayWeatherBonus > 1.35 then
		dayWeatherBonus = 1.35;
	end

	dmg = math.floor(dmg * dayWeatherBonus);

	local mab = 1;
	if (params ~= nil and params.bonusmab ~= nil) then
		mab = (100 + caster:getMod(MOD_MATT) + params.bonusmab) / (100 + target:getMod(MOD_MDEF));
	elseif (params == nil or (params ~= nil and params.includemab == true)) then
		mab = (100 + caster:getMod(MOD_MATT)) / (100 + target:getMod(MOD_MDEF));
	end

	if(mab < 0) then
		mab = 0;
	end

	dmg = math.floor(dmg * mab);

	-- print(affinityBonus);
	-- print(speciesReduction);
	-- print(dayWeatherBonus);
	-- print(burst);
	-- print(mab);
	-- print(magicDmgMod);

	return dmg;
end;

-- -------------------------------------------------------------------
--																	--
--		== Elemental Debuff Potency functions ==					--
--																	--
--		Author: ReaperX												--
--																	--
-- -------------------------------------------------------------------

function getElementalDebuffDOT(INT)
	local DOT = 0;
	if (INT<= 39) then
		DOT = 1;
	elseif (INT <= 69) then
		DOT = 2;
	elseif (INT <= 99) then
		DOT = 3;
	elseif (INT <= 149) then
		DOT = 4;
	else
		DOT = 5;
	end
	return DOT;
end;

function getElementalDebuffStatDownFromDOT(dot)
	local stat_down = 0;
	if (dot == 1) then
		stat_down = 5;
	elseif (dot == 2) then
		stat_down = 7;
	elseif (dot == 3) then
		stat_down = 9;
	elseif (dot == 4) then
		stat_down = 11;
	else
		stat_down = 13;
	end
	return stat_down;
end;

-- -------------------------------------------------------------------
--																	--
--		== getHelixDuration ==	Time of Helix Spell					--
--																	--
--	Dark Arts will further increase Helix duration, but testing is	--
--	ongoing.														--
--																	--
-- -------------------------------------------------------------------

function getHelixDuration(caster)

	local casterLevel = caster:getMainLvl();
	local duration = 30; --fallthrough
	if(casterLevel <= 39) then
		duration = 30;
	elseif(casterLevel <= 59) then
		duration = 60;
	elseif(casterLevel <= 99) then
		duration = 90;
	end
	return duration;
end;

-- -------------------------------------------------------------------
--																	--
--		== isHelixSpell ==	Is this spell a Helix?					--
--																	--
-- -------------------------------------------------------------------

function isHelixSpell(spell)

	local id = spell:getID();
	if id >= 278 and id <= 285 then
		return true;
	end
	return false;
end;

-- -------------------------------------------------------------------
--																	--
--		== handleThrenody ==	Calculate Bard Song Threnody		--
--																	--
-- -------------------------------------------------------------------

function handleThrenody(caster, target, spell, basePower, baseDuration, modifier)
	-- Process resitances
	local staff = AffinityBonus(caster, spell:getElement());
	-- print("staff=" .. staff);
	local dCHR = (caster:getStat(MOD_CHR) - target:getStat(MOD_CHR));
	-- print("dCHR=" .. dCHR);
	local resm = applyResistance(caster, spell, target, dCHR, SINGING_SKILL, staff);
	-- print("rsem=" .. resm);

	if(resm < 0.5) then
		-- print("resm resist");
		spell:setMsg(85);
		return EFFECT_THRENODY;
	end

	-- Remove previous Threnody
	target:delStatusEffect(EFFECT_THRENODY);

	local iBoost = caster:getMod(MOD_THRENODY) + caster:getMod(MOD_ALL_SONGS);
	local power = basePower + iBoost*5;
	local duration = baseDuration * ((iBoost * 0.1) + (caster:getMod(MOD_SONG_DURATION)/100) + 1);

	if (caster:hasStatusEffect(EFFECT_SOUL_VOICE)) then
		power = power * 2;
	elseif (caster:hasStatusEffect(EFFECT_MARCATO)) then
		power = power * 1.5;
	end

	if (caster:hasStatusEffect(EFFECT_TROUBADOUR)) then
		duration = duration * 2;
	end

	-- Set spell message and apply status effect
	target:addStatusEffect(EFFECT_THRENODY, power, 0, duration, 0, modifier, 0);
	return EFFECT_THRENODY;
end;

-- -------------------------------------------------------------------
--																	--
--		== handleNinjutsuDebuff ==	Calculate Ninjutsu Debuffs		--
--																	--
-- -------------------------------------------------------------------

function handleNinjutsuDebuff(caster, target, spell, basePower, baseDuration, modifier)
	-- Add new
	target:addStatusEffectEx(EFFECT_NINJUTSU_ELE_DEBUFF, 0, basePower, 0, baseDuration, 0, modifier, 0);
	return EFFECT_NINJUTSU_ELE_DEBUFF;
end;

-- -------------------------------------------------------------------
--																	--
--		== canOverwrite ==	Check to overwrite a spell				--
--																	--
--	Returns true if you can overwrite the effect					--
--	Example: canOverwrite(target, EFFECT_SLOW, 25);					--
--																	--
-- -------------------------------------------------------------------

function canOverwrite(target, effect, power, mod)
	mod = mod or 1;

	local statusEffect = target:getStatusEffect(effect);

	-- Effect not found so overwrite
	if(statusEffect == nil) then
		return true;
	end

	-- Overwrite if its weaker
	if(statusEffect:getPower()*mod > power) then
		return false;
	end

	return true;
end

-- -------------------------------------------------------------------
--																	--
--		== do Nuke spells ==	Damaging magic spells				--
--																	--
--	Calculating damage from nuking spells.							--
--																	--
-- -------------------------------------------------------------------

function doElementalNuke(V,M,caster,spell,target,hasMultipleTargetReduction,resistBonus)
	return doNuke(V,M,caster,spell,target,hasMultipleTargetReduction,resistBonus,ELEMENTAL_MAGIC_SKILL,MOD_INT);
end

function doDivineNuke(V,M,caster,spell,target,hasMultipleTargetReduction,resistBonus)
	return doNuke(V,M,caster,spell,target,hasMultipleTargetReduction,resistBonus,DIVINE_MAGIC_SKILL,MOD_MND);
end

function doNinjutsuNuke(V,M,caster,spell,target,hasMultipleTargetReduction,resistBonus)
	return doNuke(V,M,caster,spell,target,hasMultipleTargetReduction,resistBonus,NINJUTSU_SKILL,MOD_INT);
end

function doNuke(V,M,caster,spell,target,hasMultipleTargetReduction,resistBonus,skill,modStat)
	-- Calculate raw damage
	local dmg = calculateMagicDamage(V,M,caster,spell,target,skill,modStat,hasMultipleTargetReduction);
	-- Get resist multiplier (1x if no resist)
	local resist = applyResistance(caster,spell,target,caster:getStat(modStat)-target:getStat(modStat),skill,resistBonus);
	-- Get the resisted damage
	dmg = dmg*resist;
	if(skill == NINJUTSU_SKILL) then
		-- Ninjutsu Mag Dmg Bonus (Koga Hatsuburi etc.)
		dmg = math.floor(dmg * (1 + (caster:getMod(MOD_NIN_MAG_DMG)/100)));
	end
	-- Add on bonuses (staff/day/weather/jas/mab/etc all go in this function)
	dmg = addBonuses(caster,spell,target,dmg);
	-- Add in target adjustment
	dmg = adjustForTarget(target,dmg,spell:getElement());
	-- Add in final adjustments
	dmg = finalMagicAdjustments(caster,target,spell,dmg);
	return dmg;
end

function doDivineBanishNuke(V,M,caster,spell,target,hasMultipleTargetReduction,resistBonus)
	local skill = DIVINE_MAGIC_SKILL;
	local modStat = MOD_MND;
	
	--calculate raw damage
	local dmg = calculateMagicDamage(V,M,caster,spell,target,skill,modStat,hasMultipleTargetReduction);
	--get resist multiplier (1x if no resist)
	local resist = applyResistance(caster,spell,target,caster:getStat(modStat)-target:getStat(modStat),skill,resistBonus);
	--get the resisted damage
	dmg = dmg*resist;
	
	--add on bonuses (staff/day/weather/jas/mab/etc all go in this function)
	dmg = addBonuses(caster,spell,target,dmg);
	--add in target adjustment
	dmg = adjustForTarget(target,dmg,spell:getElement());
	--handling afflatus misery
	dmg = handleAfflatusMisery(caster, spell, dmg);
	--add in final adjustments
	dmg = finalMagicAdjustments(caster,target,spell,dmg);
	return dmg;
end

-- -------------------------------------------------------------------
--																	--
--		== calculateDurationForLvl ==								--
--																	--
--	Calculating a duration based off level of target and spell.		--
--																	--
-- -------------------------------------------------------------------

function calculateDurationForLvl(duration, spellLvl, targetLvl)
	if(targetLvl < spellLvl) then
		return duration * targetLvl / spellLvl;
	end

	return duration;
end

-- -------------------------------------------------------------------
--																	--
--		== calculateBarspellPower ==								--
--																	--
--	Calculating the power of Bar spells.							--
--																	--
-- -------------------------------------------------------------------

function calculateBarspellPower(caster,enhanceSkill)
	if (enhanceSkill == nil or enhanceSkill < 0) then
		enhanceSkill = 0;
	end

	local power = 40;

	if(enhanceSkill > 300) then
		power = 25 + 0.25 * enhanceSkill;
	else
		power = 40 + 0.2 * enhanceSkill;
	end

	if(power > 150) then
		power = 150;
	end

	power = power + caster:getMod(MOD_BARSPELL_PWR);

	if(caster:getObjType() == TYPE_PC) then
		power = power + caster:getMerit(MERIT_BAR_SPELL_EFFECT);
	end

	return power;
end